package com.ds.infrastructure.audit.collector.interceptor;

import com.ds.infrastructure.audit.collector.annotation.RateLimit;
import com.ds.infrastructure.audit.collector.entity.RateAccess;
import com.ds.infrastructure.audit.collector.service.IRateService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * 作用：
 *
 * @author WeiShaoying
 * @date 2020/8/5
 */
@Order(2)
@Aspect
@Slf4j
@Component
public class RateLimitInterceptor {

    @Resource
    private IRateService rateService;

    @Pointcut("@annotation(com.ds.infrastructure.audit.collector.annotation.RateLimit)")
    public void rate() {
    }

    /**
     * Rate Handler
     *
     * @param joinPoint 切入点
     * @return
     */
    @Around(value = "rate()")
    public Object limit(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        log.info("====访问：{}", signature.toShortString());
        Method method = signature.getMethod();
        RateLimit rateLimit = method.getAnnotation(RateLimit.class);
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        String key = rateLimit.key();
        int window = rateLimit.window();
        int frequency = rateLimit.frequency();
        try {
            boolean b = rateService.canAccess(RateAccess.of(rateLimit.prefix(), StringUtils.isNotBlank(key) ? key : StringUtils.join(args, "_"), window, frequency));
            if (b) {
                return joinPoint.proceed();
            } else {
                throw new RuntimeException("Call Speed Too Fast");
            }
        } catch (Throwable e) {
            throw new RuntimeException(e.getLocalizedMessage());
        }
    }

}
